/*
 * oledDriver.c
 *
 * Created: 24.09.2012 10:23:30
 *  Author: vegardvo
 */ 
#include "oledDriver.h"
#include "font_8x8.h"
#include <avr/pgmspace.h>
#include <string.h>



void oledDriver_write_command(char command)
{
	volatile char *oled = (char*) OLED_COMMAND_ADDRESS;
	oled[0] = command;
}

void oledDriver_write_char(char character)
{
	for(int i = 0; i < 8; i++){
		oledDriver_write_data(pgm_read_byte(&font[character - 32][i]));
	}
}

void oledDriver_write_inverted_char(char character)
{
	for(int i = 0; i < 8; i++){
		oledDriver_write_data(~(pgm_read_byte(&font[character - 32][i])));
	}
}

void oledDriver_select_page(int page)
{
	if (page > 7)
	{
		printf("ERROR: oledDriver_select_page: page > 7\n");
	}
	else if (page < 0)
	{
		printf("ERROR: oledDriver_select_page: page < 0\n");
	}
	else
	{
		oledDriver_write_command(0xB0 + page);
	}
	
}

void oledDriver_select_segment(int segment)
{
	if (segment > 127)
	{
		printf("ERROR: oledDriver_select_segment: segment > 127\n");
	}
	else if (segment < 0)
	{
		printf("ERROR: oledDriver_select_segment: segment < 0\n");
	}
	else
	{
		int upperNibble = segment%15;
		if(segment > 15)
		{
			upperNibble = (segment - 1)%15;
		}
		int lowerNibble = ((segment)/16);
		oledDriver_write_command(0x00 + upperNibble);
		oledDriver_write_command(0x10 + lowerNibble);
	}
	
}

void oledDriver_clear_screen()
{
	for(int j = 0; j < 8; j++)
	{
		oledDriver_write_command(0xB0 + j);
		for(int i = 0; i < 128; i++)
		{
			oledDriver_write_data(0x00);
		}			
	}
	oledDriver_select_page(0);
	oledDriver_select_segment(0);
}

void oledDriver_clear_line(int line)
{
	oledDriver_select_page(line);
	oledDriver_select_segment(0);
	for(int i = 0; i < 16; i++)
	{
		oledDriver_write_char(' ');
	}
}

void oledDriver_write_string(char* string, int inverted,int centered)
{
	int segment = 0;
	if(centered)
	{
		segment = 64 - strlen(string)*4;
		/*int a = segment%15;
		if(segment > 15)
		{
			a = (segment - 1)%15;
		}
		int b = ((segment)/16);
		printf("Lengde: %d, Segment: %d, a = %d, b = %d\n", strlen(string), segment, a, b);
		*/
	}
	oledDriver_select_segment(segment);
	int i = 0;
	while(string[i] != '\0')
	{
		if(inverted)
		{
			oledDriver_write_inverted_char(string[i]);	
		}
		else
		{
			oledDriver_write_char(string[i]);
		}
		i++;
		if(i > 15)
		{
			break;
		}
	}
}

void oledDriver_set_position(int line, int column)
{
	oledDriver_select_page(line);
	oledDriver_select_segment(column*8);
}

void oledDriver_write_data(char data)
{
	volatile char *oled = (char*) OLED_DATA_ADDRESS;
	oled[0] = data;
}

void oledDriver_init()
{
	oledDriver_write_command(0xAE); // display off
	oledDriver_write_command(0xA1); //segment remap
	oledDriver_write_command(0xDA); //common pads hardware: alternative
	oledDriver_write_command(0x12);
	oledDriver_write_command(0xC8); //common output scan direction:com63~com0
	oledDriver_write_command(0xA8); //multiplex ration mode:63
	oledDriver_write_command(0x3F);
	oledDriver_write_command(0xD5); //display divide ratio/osc. freq. mode
	oledDriver_write_command(0x80);
	oledDriver_write_command(0x81); //contrast control
	oledDriver_write_command(0x50);
	oledDriver_write_command(0xD9); //set pre-charge period
	oledDriver_write_command(0x21);
	oledDriver_write_command(0x20); //Set Memory Addressing Mode
	oledDriver_write_command(0x02);
	oledDriver_write_command(0xDB); //VCOM deselect level mode
	oledDriver_write_command(0x30);
	oledDriver_write_command(0xAD); //master configuration
	oledDriver_write_command(0x00);
	oledDriver_write_command(0xA4); //out follows RAM content
	oledDriver_write_command(0xA6); //set normal display
	oledDriver_write_command(0xAF); // display on
	oledDriver_clear_screen();
}